/*
 * PPMC.cpp
 *
 *  Created on: 11/06/2013
 *      Author: pato
 */

#include "PPMC.h"
#include <iostream>
// El compresor sirve para una compresion o para una descompresion y luego debe llamarse al delete
// No queda en un estado valido para ser reutilizado.
PPMC::PPMC() {
	this->compresor = new Aritmetico();
	this->ultimo_modelo = 1;           		// Se toma el modelo 1 como el 0,
	this->modelos[0] = new Modelo_0(); 		// Y el modelo 0 como el -1.
	for(int i = 1; i <= ORDEN_PPMC + 1; i++) {
		this->modelos[i] = new Modelo(i);
	}
	this->contexto = "";
}
/*
 *	buffer: es un puntero al array a descomprimir
 * 	largo: es el largo del buffer a descomprimir
 *  descompresion: es el buffer con la cadena descomprimida
 *  largo_descompresion: es el largo del array descomprimido
 *  Precondicion:
 *  				* El buffer debe haber sido mallocado previamente y estar cargado
 *  					con el  string a descomprimir.
 *  Postcondicion:
 *  				* El ppmc , realiza el malloc de "descompresion" y retorna un puntero a
 *  				  ese bloque de memoria.
 *  				* El ppmc no libera la memoria del buffer.
 */
void PPMC::descomprimirBuffer(char *buffer, int largo,char **descompresion,int *largo_descompresion) {
	//Inicia la descompresion en el aritmetico, crea un contexto nulo
	this->compresor->setup_descompresion(buffer);
	this->contexto = "";
	string c = "";
	*descompresion = (char*) malloc (MB1);
	if (!*descompresion) {
		cout<<"no pudo reserbar memomira grrrr"<< endl;
	}
	Caracter* caracter = NULL;
	int iterator = 0;
	do {
		if (iterator >1 ) { delete caracter;}
		caracter = this->descomprimirCaracter();
		if (!(*caracter=="EOF")) {
			iterator++;
			caracter->getDato(c);
			this->contexto.append(c);
		}
		if (this->ultimo_modelo < ORDEN_PPMC + 1) {
			this->ultimo_modelo++;
		}
	} while( !(*caracter=="EOF")); //NO es el de fin de archivo
	delete caracter;
	*largo_descompresion = iterator;
	strcpy(*descompresion,this->contexto.c_str());
}

Caracter* PPMC::descomprimirCaracter() {
	Caracter* caracter = NULL;
	int iterator = this->ultimo_modelo+1;
	int rta = 0;
	do {
		//Falta crear un modelo en caso de que no exista. En la prueba que estamos haciendo se puede porque los modelos ya fueron creados antes?
		iterator--;
		caracter = this->modelos[iterator]->buscarCaracter(this->compresor,this->contexto);
		if (*caracter == "ESC" ){
			//Si la lista del contexto no es 1, hay que actualizar.
			this->modelos[iterator]->AgregarCaracter(caracter,this->contexto);
		}
	}while(*caracter=="ESC");

	for (; iterator <= this->ultimo_modelo; iterator++) {
		rta = this->modelos[iterator]->AgregarCaracter(caracter,this->contexto);
		if (rta != 0) {
			cout << "Esto no deberia pasar"<<endl;
			exit(1);
		}
	}

	return caracter;
}

/*
 *	buffer: es un puntero al array a comprimir
 * 	largo: es el largo del buffer a comprimir
 *  compresion: es el buffer con la cadena comprimida
 *  largo_compresion: es el largo del array comprimido
 *  Precondicion:
 *  				* El buffer debe haber sido mallocado previamente con un tamanyo
 *  					mayor o igual a "largo".
 *  				* El largo debe ser mayor igual a 1.
 *  Postcondicion:
 *  				* El ppmc , realiza el malloc de "compresion" y retorna un puntero a
 *  				  ese bloque de memoria.
 *  				* El ppmc no libera la memoria del buffer.
 */
void PPMC::comprimirBuffer(char *buffer, int largo,char **compresion,int *largo_compresion) {
	for(int i = 0; i < largo; i++) {
		this->comprimir(&buffer[i]);
	}
	this->finalizar();
	this->compresor->getBuffer(compresion, largo_compresion);
}

void PPMC::finalizar() {
	int iteraciones = 0;
	while((this->modelos[this->ultimo_modelo - iteraciones])->TerminarCompresion(this->contexto, this->compresor) != 0) {
		iteraciones++;
	}
}
/*
 * Comprime un caracter
 */
void PPMC::comprimir(char* caracter) {
	int iteraciones = 0;
	this->contexto.push_back(*caracter);
	while((this->modelos[this->ultimo_modelo - iteraciones])->ComprimirCaracter(this->contexto, this->compresor) != 0) {
		iteraciones++;
	}
	if (this->ultimo_modelo < ORDEN_PPMC + 1) {
		this->ultimo_modelo++;
	}
}


PPMC::~PPMC() {
	delete this->compresor;
	for(int i = 0; i <= ORDEN_PPMC+1; i++) {
			delete this->modelos[i];
	}
}

